﻿namespace ScrumLite.WebUx.Controllers
{
    using System;
    using System.Configuration;
    using System.Collections.ObjectModel;
    using System.Linq;
    using System.Web.Mvc;
    using GoogleChartSharp;
    using ScrumLite.Services;
    using ScrumLite.Services.Contracts;
    using ScrumLite.WebUx.Models;
    using GoogleChartSharp.CustomCharts;
    using ScrumLite.Gateways;
    using System.Globalization;
    using System.Web.Routing;

    public class DefectsController : ContextBaseController
    {
        IDefectsService defectsService;
        IDeliverablesService deliverables;

        public DefectsController()
            : this(new DefectsServiceClient(), new ProjectsServiceClient(), new DeliverablesServiceClient(), new ProfileServiceClient())

        {

        }

        public DefectsController(IDefectsService defects, IProjectsService projects, IDeliverablesService deliverables, IProfileService profile) : base(profile, projects)
        {
            this.defectsService = defects;
            this.deliverables = deliverables;
        }

        public void Read(string projectId, string mode)
        {
            byte triaged = 1;
            if (HttpContext != null && HttpContext.Session != null && HttpContext.Session["DefectsTriaged"] != null && HttpContext.Session["DefectsProjectUri"] != null && HttpContext.Session["DefectsProjectUri"].Equals(projectId))
            {
               triaged = (byte)HttpContext.Session["DefectsTriaged"];
            }

            if (HttpContext != null && HttpContext.Session != null && HttpContext.Session["DefectsTriaged"] != null)
            {
                HttpContext.Session.Remove("DefectsTriaged");
            }

            if (HttpContext != null && HttpContext.Session != null && HttpContext.Session["DefectsProjectUri"] != null)
            {
                HttpContext.Session.Remove("DefectsProjectUri");
            }


            string viewToRender;
            switch (mode.ToUpperInvariant())
            {
                case "ALL": viewToRender = "ReadAll";
                    break;
                default:
                    throw new ArgumentException("Unknow mode:"+mode);
            }

            DefectsReadViewData data = new DefectsReadViewData()
            {
                ProjectName = Projects.GetProjectName(projectId),
                ProjectId = projectId,
                Mode = mode,
                DefectsView = viewToRender,
                Triaged = triaged,
                ViewTitle = "Defects"
            };

            RenderView("Read", data);
        }

        public void ReadByDeliverable(string projectId, string deliverableId)
        {
            string viewToRender = "ReadAll";
            DefectsReadViewData data = new DefectsReadViewData()
            {
                ProjectName = Projects.GetProjectName(projectId),
                ProjectId = projectId,
                Mode = "ALL",
                DefectsView = viewToRender,
                ViewTitle = "Defects"
            };

            data.DeliverableId = deliverableId;

            RenderView("Read", data);        
        }

        public void ReadOne(string projectId, int moniker)
        {
            Defect defect = defectsService.GetDefect(projectId, moniker);
            DefectsWriteViewData data = new DefectsWriteViewData()
            {
                ProjectName = Projects.GetProjectName(projectId),
                ProjectId = projectId,
                Defect = defect,
                ViewTitle = "Edit Defect"
            };
            foreach (Deliverable deliverable in this.deliverables.GetDeliverables(projectId))
            {
                data.Deliverables.Add(deliverable.Moniker, deliverable.FullName);
                if (defect.DeliverableId == deliverable.Id)
                {
                    data.DeliverableMoniker = deliverable.Moniker;    
                }
            };

            RenderView("Write", data);
        }

        public void New(string projectId, byte triaged)
        {
            if (HttpContext != null && HttpContext.Session != null)
            {
                HttpContext.Session["DefectsTriaged"] = triaged;
                HttpContext.Session["DefectsProjectUri"] = projectId;
            }
            DefectsWriteViewData data = new DefectsWriteViewData()
            {              
                ProjectName = Projects.GetProjectName(projectId),
                ProjectId = projectId,
                ViewTitle = "Add New Defect"
            };
            foreach (Deliverable deliverable in this.deliverables.GetDeliverables(projectId))
            {                            
                data.Deliverables.Add(deliverable.Moniker, deliverable.FullName);
            };


            RenderView("Write", data);
        }

        public void Write(string projectId, int moniker, string id, int deliverableMoniker, string title, string notes, string decision, DateTime createdOn, string createdBy, int severity)
        {
            Defect defectToSave = new Defect()
            {
                DeliverableId = deliverables.GetDeliverableByMoniker(projectId, deliverableMoniker),
                Title = title,
                Notes = notes,
                Decision = decision,
                CreatedOn=createdOn,
                CreatedBy = createdBy,
                Severity = severity
            };

            if (string.IsNullOrEmpty(id))
            {
                defectToSave.Moniker = defectsService.GetNextMoniker(projectId);
                defectToSave.Id = Guid.NewGuid();
                defectsService.AddDefect(defectToSave);

            }
            else
            {
                defectToSave.Moniker = moniker;
                defectToSave.Id = new Guid(id);

                defectsService.UpdateDefect(defectToSave);
            }

            RedirectToAction(new { Action = "Read", Mode = "All" });
        }

        public void DefectsMetrics(string projectId)
        {
            Collection<Defect> defects = defectsService.GetDefects(projectId, 1);
            DefectsMetricsData data = new DefectsMetricsData()
            {
                DefectsTriaged = defects.Count(defect => defect.Triaged),
                DefectsNotTriaged = defects.Count(defect => !defect.Triaged),
                ToTriage0To1 = defects.Count(defect => DaysToTriage(defect) <= 1),
                ToTriage1To5 = defects.Count(defect => DaysToTriage(defect) > 1
                    && DaysToTriage(defect) <= 5),
                ToTriage5To20 = defects.Count(defect => DaysToTriage(defect) > 5
                    && DaysToTriage(defect) <= 20),
                ToTriageMoreThan20 = defects.Count(defect => DaysToTriage(defect) > 20),
                ViewTitle = "Defects Metrics"
            };

            data.GChartTimeToTriagePie = CreateTimeToTriagePieUrl(data);
            data.GChartTimeToTriageBars = CreateTimeToTriageBarsUrl(data);
            data.GChartDefectsPie = CreateDefectsPieUrl(data);
            data.GChartDefectsBars = CreateDefectsBarsUrl(data);
            data.ProjectName = Projects.GetProjectName(projectId);
            data.ProjectId = projectId;
            RenderView("DefectsMetrics", data);
        }

        #region Google Charts helper methods
        private static string CreateDefectsBarsUrl(DefectsMetricsData data)
        {
            if (data.DefectsTriaged == 0 && data.DefectsNotTriaged == 0)
            {
                return String.Format(CultureInfo.InvariantCulture, "/{0}/{1}", ConfigurationManager.AppSettings["SiteImagesPath"], "chart_EmptyDefectsBars.png");
            }
            else
            {
                BarChart chartBuilder = new BarChart(400, 150, BarChartOrientation.Vertical, BarChartStyle.Grouped);
                chartBuilder.SetBarWidth(20, 5, 50);
                chartBuilder.SetTitle("Defects");

                ChartAxis timeAxis = new ChartAxis(ChartAxisType.Bottom, new string[] {
                Uri.EscapeUriString("Triaged"),
                Uri.EscapeUriString("Not triaged")});
                chartBuilder.AddAxis(timeAxis);

                float maxValueRange = Math.Max(data.DefectsTriaged, data.DefectsNotTriaged);
                int maxValueLabel = (Convert.ToInt32((maxValueRange) / 10) + 1) * 10;
                ChartAxis unitAxis = new ChartAxis(ChartAxisType.Left);
                unitAxis.SetRange(0, maxValueLabel);
                chartBuilder.AddAxis(unitAxis);

                float[] chartData = new float[] { data.DefectsTriaged, data.DefectsNotTriaged };
                chartBuilder.SetData(chartData, maxValueLabel);

                return chartBuilder.GetUrl();
            }
        }

        private static string CreateDefectsPieUrl(DefectsMetricsData data)
        {
            if (data.DefectsTriaged == 0 && data.DefectsNotTriaged == 0)
            {
                return String.Format(CultureInfo.InvariantCulture,"/{0}/{1}", ConfigurationManager.AppSettings["SiteImagesPath"], "chart_EmptyDefectsPie.png");
            }
            else
            {
                LabeledValuePieChart chartBuilder = new LabeledValuePieChart(450, 150, PieChartType.ThreeD);
                chartBuilder.SetTitle("Defects");
                chartBuilder.SetPieChartLabels(new string[] { 
                    "Defects triaged", 
                    "Defects not triaged"});
                chartBuilder.SetData(new float[] { data.DefectsTriaged, data.DefectsNotTriaged });

                return chartBuilder.GetUrl();
            }
        }

        private static string CreateTimeToTriageBarsUrl(DefectsMetricsData data)
        {
            if (data.ToTriage0To1 == 0 && data.ToTriage1To5 == 0 && data.ToTriage5To20 == 0 && data.ToTriageMoreThan20 == 0)
            {
                return String.Format(CultureInfo.InvariantCulture, "/{0}/{1}", ConfigurationManager.AppSettings["SiteImagesPath"], "chart_EmptyTimeToTriageBars.png");
            }
            else
            {
                BarChart chartBuilder = new BarChart(400, 150, BarChartOrientation.Vertical, BarChartStyle.Grouped);
                chartBuilder.SetBarWidth(20, 5, 50);
                chartBuilder.SetTitle("Time to triage");

                ChartAxis timeAxis = new ChartAxis(ChartAxisType.Bottom, new string[] {
                    Uri.EscapeUriString("0-1"),
                    Uri.EscapeUriString("1-5"),
                    Uri.EscapeUriString("5-20"),
                    Uri.EscapeUriString(">20") });
                chartBuilder.AddAxis(timeAxis);

                float maxValueRange = Math.Max(data.ToTriage0To1, Math.Max(data.ToTriage1To5, Math.Max(data.ToTriage5To20, data.ToTriageMoreThan20)));
                int maxValueLabel = (Convert.ToInt32((maxValueRange) / 10) + 1) * 10;
                ChartAxis unitAxis = new ChartAxis(ChartAxisType.Left);
                unitAxis.SetRange(0, maxValueLabel);
                chartBuilder.AddAxis(unitAxis);

                float[] chartData = new float[] { data.ToTriage0To1, data.ToTriage1To5, data.ToTriage5To20, data.ToTriageMoreThan20 };
                chartBuilder.SetData(chartData, maxValueLabel);

                return chartBuilder.GetUrl();
            }
        }

        private static string CreateTimeToTriagePieUrl(DefectsMetricsData data)
        {
            if (data.ToTriage0To1 == 0 && data.ToTriage1To5 == 0 && data.ToTriage5To20 == 0 && data.ToTriageMoreThan20 == 0)
            {
                return String.Format(CultureInfo.InvariantCulture,"/{0}/{1}", ConfigurationManager.AppSettings["SiteImagesPath"], "chart_EmptyTimeToTriagePie.png");
            }
            else
            {
                LabeledValuePieChart chartBuilder = new LabeledValuePieChart(450, 150, PieChartType.ThreeD);
                chartBuilder.SetTitle("Time to triage");
                chartBuilder.SetPieChartLabels(new string[] { 
                    "0 to 1 day", 
                    "1 to 5 days", 
                    "5 to 20 days", 
                    "more than 20 days" });
                chartBuilder.SetData(new float[] { data.ToTriage0To1, data.ToTriage1To5, data.ToTriage5To20, data.ToTriageMoreThan20 });

                return chartBuilder.GetUrl();
            }
        }

        private static int DaysToTriage(Defect defect)
        {
            DateTime TriagedOrNow = (defect.TriagedOn ?? DateTime.Now);
            TimeSpan timeElapsed = TriagedOrNow.Subtract(defect.CreatedOn);
            return timeElapsed.Days;
        }
        #endregion
    }
}
